home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / c / metaphn.com / METAPHON.C < prev   
Encoding:
C/C++ Source or Header  |  1991-06-11  |  7.1 KB  |  239 lines

  1. /*-------------------------- METAPHON.C -------------------
  2. *
  3. * Author: Gary Parker
  4. * Compilers: Borland C++ 2.0, Turbo C++, Turbo C 2.0, MSC 6.0
  5. * Memory model: any
  6. *
  7. * This is an implementation of the Metaphone algorithm developed
  8. * by Lawrence Philips.  Like the Soundex algorithm, it compares
  9. * words that sound alike but are spelled differently.  Metaphone
  10. * was designed to overcome difficulties encountered with Soundex.
  11. *
  12. * Usage:
  13. * The calling function must pass three arguments:
  14. *
  15. *     char *Word    - the word to be converted to a 'metaph'
  16. *     char *Metaph  - a MAXMETAPH+1 byte field for the result
  17. *     int   Flag    - a flag
  18. *
  19. * If flag is 1 then a 'metaph' will be computed for Word and
  20. * stored in Metaph.  If flag is 0, then the function will compute
  21. * a 'metaph' for Word and compare it with the 'metaph', passed in
  22. * Metaph.  It will return 0 for a match, else -1.  The function
  23. * will also return -1 if Word is 0 bytes long.
  24. *
  25. * This code is hereby placed in the public domain.
  26. *----------------------------------------------------------------------*/
  27.  
  28. /*------------Metaphone Transformations----------------------------------
  29. * PN- GN- KN-   N
  30. * AE-           E
  31. * WR-           R
  32. * WH-           H
  33. * X-            S
  34. *
  35. * B             B (unless in -MB)
  36. * C             X if in -CIA-, -CH-
  37. *                             else S if in -CI-, -CE-, -CY-
  38. *                             else dropped if in -SCI-, -SCE-, -SCY-
  39. *                             else K
  40. * D             J if in -DGE-, -DGI-, -DGY-
  41. *                             else T
  42. * G             F if in -GH and not B--GH, D--GH,-H--GH, -H---GH
  43. *                             else dropped if -GNED, -GN, -DGE-, -DGI-, -DGY-
  44. *                             else J if in -GE-, -GI-, -GY- and not GG
  45. *                             else K
  46. * H             H if before a vowel and not after C, G, P, S, T
  47. *                             else dropped
  48. * K             dropped if after C, else K
  49. * P             F if before H, else P
  50. * Q             K
  51. * S             X in -SIO- or -SIA-, else S
  52. * T             X in -TIA- or -TIO-
  53. *                             else O before H
  54. *                             else T
  55. * V             F
  56. * W             W after a vowel, else dropped
  57. * X             KS
  58. * Y             Y unless followed by a vowel
  59. * Z             S
  60. *
  61. * Note: F, J, L, M, N, R are never transformed.
  62. *
  63. *----------------------------------------------------------------------*/
  64.  
  65. #include <ctype.h>
  66.  
  67. #define MAXMETAPH  4
  68.  
  69. int metaphone(char *,char *, int);
  70.  
  71. /* Character coding array */
  72. static char vsvfn[26] = {
  73.         1,16,4,16,9,2,4,16,9,2,0,2,2,2,1,4,0,2,4,4,1,0,0,0,8,0};
  74. /*  A  B C  D E F G  H I J K L M N O P Q R S T U V W X Y Z */
  75.  
  76. /* Macros to access character coding array */
  77. #define vowel(x)  (vsvfn[(x) - 'A'] & 1)  /* AEIOU */
  78. #define same(x)   (vsvfn[(x) - 'A'] & 2)  /* AEIOU */
  79. #define varson(x) (vsvfn[(x) - 'A'] & 4)  /* AEIOU */
  80. #define frontv(x) (vsvfn[(x) - 'A'] & 8)  /* AEIOU */
  81. #define noghf(x)  (vsvfn[(x) - 'A'] & 16) /* AEIOU */
  82.  
  83. int metaphone(char *Word, char *Metaph, int Flag)
  84. {
  85.     char *n, *n_start, *n_end;  /* pointers to string */
  86.     char *metaph, *metaph_end;  /* pointers to metaph */
  87.     char ntrans[32];            /* word with uppercase letters */
  88.     char newm[8];               /* new metaph for comparison */
  89.     int KSflag;                 /* state flag for X translation */
  90.  
  91.     /*
  92.     ** Copy Word to internal buffer, dropping non-alphabetic
  93.     ** charactes and converting to uppercase.
  94.     */
  95.     for(n = ntrans + 1, n_end = ntrans + 30;
  96.         *Word && n < n_end; Word++)
  97.         if(isalpha(*Word)) *n++ = toupper(*Word);
  98.  
  99.     if(n == ntrans + 1) return -1;  /* return if 0 bytes */
  100.     n_end = n;                      /* set n_end to end of string */
  101.  
  102.     /* ntrans[0] will always be == 0 */
  103.     *n++ = 0; *n = 0; /* pad with nulls */
  104.     n = ntrans + 1;   /* assign pointer to start */
  105.  
  106.     /* if doing a comparision, redirect pointers */
  107.     if(!Flag) { metaph = Metaph; Metaph = newm; }
  108.  
  109.     /* check for PN KN GN AE WR WH and X at start */
  110.     switch(*n) {
  111.         case 'P': case 'K': case 'G':
  112.             if(*(n + 1) == 'N') *n++ = 0;
  113.             break;
  114.         case 'A':
  115.             if(*(n + 1) == 'E') *n++ = 0;
  116.             break;
  117.         case 'W':
  118.             if(*(n + 1) == 'R') *n++ = 0;
  119.             else if(*(n + 1) == 'H') {
  120.                 *(n + 1) = *n;
  121.                 *n++ = 0;
  122.             }
  123.             break;
  124.         case 'X':
  125.             *n = 'S';
  126.             break;
  127.     }
  128.  
  129.     /*
  130.     ** Now, loop step through string, stopping at end of string
  131.     ** or when the computed 'metaph' is MAXMETAPH characters long
  132.     */
  133.     KSflag = 0; /* state flag for KS translation */
  134.     for(metaph_end = Metaph + MAXMETAPH, n_start = n;
  135.         n <= n_end && Metaph < metaph_end; n++) {
  136.  
  137.         if (KSflag) {
  138.             KSflag = 0;
  139.             *Metaph++= *n;
  140.         }
  141.         else {
  142.             /* drop duplicates except for CC */
  143.             if(*(n - 1) == *n && *n != 'C') continue;
  144.  
  145.             /* check for F J L M N R or first letter vowel */
  146.             if(same(*n) || (n == n_start && vowel(*n)))
  147.                 *Metaph++ = *n;
  148.             else switch(*n) {
  149.                 case 'B':
  150.                     if(n < n_end || *(n - 1) != 'M')
  151.                         *Metaph++ = *n;
  152.                     break;
  153.                 case 'C':
  154.                     if(*(n - 1) != 'S' || !frontv(*(n + 1))) {
  155.                         if(*(n + 1) == 'I' && *(n + 2) == 'A')
  156.                             *Metaph++ = 'X';
  157.                         else if(frontv(*(n + 1))) *Metaph++ = 'S';
  158.                         else if(*(n + 1) == 'H')
  159.                             *Metaph++ = ((n == n_start &&
  160.                             !vowel(*(n + 2))) ||
  161.                             *(n - 1) == 'S')
  162.                             ? (char)'K' : (char)'X';
  163.                         else *Metaph++ = 'K';
  164.                     }
  165.                     break;
  166.                 case 'D':
  167.                     *Metaph++ = (*(n + 1) == 'G' && frontv(*(n + 2)))
  168.                         ? (char)'J' : (char)'T';
  169.                     break;
  170.                 case 'G':
  171.                     if((*(n + 1) != 'H' || vowel(*(n + 2))) &&
  172.                         (*(n + 1) != 'N' || ((n + 1) < n_end &&
  173.                         (*(n + 2) != 'E' || *(n + 3) != 'D'))) &&
  174.                         (*(n - 1) != 'D' || !frontv(*(n + 1))))
  175.                         *Metaph++ = (frontv(*(n + 1)) &&*(n + 2) != 'G')
  176.                         ? (char)'J' : (char)'K';
  177.                     else if(*(n + 1) == 'H' && !noghf(*(n - 3))
  178.                         && *(n - 4) != 'H') *Metaph++ = 'F';
  179.                     break;
  180.                 case 'H':
  181.                     if(!varson(*(n - 1)) && (!vowel(*(n - 1)) ||
  182.                         vowel(*(n + 1)))) *Metaph++ = 'H';
  183.                     break;
  184.                 case 'K':
  185.                     if(*(n - 1) != 'C') *Metaph++ = 'K';
  186.                     break;
  187.                 case 'P':
  188.                     *Metaph++ = *(n + 1) == 'H'
  189.                         ? (char)'F' : (char)'P';
  190.                     break;
  191.                 case 'Q':
  192.                     *Metaph++ = 'K';
  193.                     break;
  194.                 case 'S':
  195.                     *Metaph++ = (*(n + 1) == 'H' || (*(n + 1) == 'I'
  196.                         && (*(n + 2) == 'O' || *(n + 2) == 'A')))
  197.                         ? (char)'X' : (char)'S';
  198.                     break;
  199.                 case 'T':
  200.                     if(*(n + 1) == 'I' && (*(n + 2) == 'O'
  201.                         || *(n + 2) == 'A'))
  202.                         *Metaph++ = 'X';
  203.                     else if(*(n + 1) == 'H') *Metaph++ = 'O';
  204.                     else if(*(n + 1) != 'C' || *(n + 2) != 'H')
  205.                         *Metaph++ = 'T';
  206.                     break;
  207.                 case 'V':
  208.                     *Metaph++ = 'F';
  209.                     break;
  210.                 case 'W': case 'Y':
  211.                     if(vowel(*(n + 1))) *Metaph++ = *n;
  212.                     break;
  213.                 case 'X':
  214.                     if(n == n_start) *Metaph++ = 'S';
  215.                     else {
  216.                         *Metaph++ = 'K'; /* insert K, then S */
  217.                         KSflag = 1; /* this flag will cause S to be
  218.                                                      inserted on next pass thru loop */
  219.                     }
  220.                     break;
  221.                 case 'Z':
  222.                     *Metaph++ = 'S';
  223.                     break;
  224.             }
  225.         }
  226.  
  227.         /* compare new metaph with old */
  228.         if(!Flag && *(Metaph - 1) != metaph[(Metaph - newm) - 1])
  229.             return -1;
  230.     }
  231.  
  232.     /* If comparing, check if metaphs were equal in length. */
  233.     if(!Flag && metaph[Metaph - newm])
  234.         return -1;
  235.  
  236.     *Metaph = 0; /* null terminate return value */
  237.     return 0;
  238. }
  239.